package com.wb.creational.prototype;

import lombok.Data;
import lombok.ToString;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * @author 魏彪
 */
public class PrototypeTest {
    public static void main(String[] args) {
        // 创建原型对象
        // ConcretePrototypeB prototypeB = new ConcretePrototypeB("origin");
        // System.out.println(prototypeB);
        // 复制原型对象
        // ConcretePrototypeB cloneTypeB = prototypeB.clone();
        // cloneTypeB.desc = "clone";
        // System.out.println(cloneTypeB);
//        ConcretePrototype prototype = new ConcretePrototype("original");
//        System.out.println(prototype);
//
//        ConcretePrototype cloneType = prototype.clone();
//        System.out.println(cloneType);
//
//        System.out.println(prototype == cloneType);

        // 浅拷贝
//        ConcretePrototypeA prototypeA = new ConcretePrototypeA();
//        prototypeA.setAge(18);
//        prototypeA.setName("Tom");
//
//        List<String> hobbies = new ArrayList<>();
//        hobbies.add("书法");
//        hobbies.add("美术");
//        prototypeA.setHobbies(hobbies);
//
//        System.out.println(prototypeA);
//
//        // 复制原型对象
//        ConcretePrototypeA cloned = prototypeA.clone();
//        cloned.getHobbies().add("技术控");
//
//        System.out.println("原型对象" + prototypeA);
        // 深拷贝
        // 创建原型对象
        ConPrototypeDeep prototypeDeep = new ConPrototypeDeep();
        prototypeDeep.setAge(18);
        prototypeDeep.setName("Tom");
        List<String> hobbies = new ArrayList<>();
        hobbies.add("书法");
        hobbies.add("美术");
        prototypeDeep.setHobbies(hobbies);

        // 复制原型对象
        ConPrototypeDeep deepClone = prototypeDeep.deepClone();
        deepClone.getHobbies().add("技术控");

        System.out.println("prototype = " + prototypeDeep);
        System.out.println("deepClone = " + deepClone);


    }
}

interface IPrototype<T> {
    T clone();
}

class ConcretePrototypeB implements IPrototype<ConcretePrototypeB> {

    String desc;

    public ConcretePrototypeB(String desc) {
        this.desc = desc;
    }

    @Override
    public ConcretePrototypeB clone() {
        // 进行复制
        return new ConcretePrototypeB(this.desc);
    }

    @Override
    public String toString() {
        return "ConcretePrototypeB{" +
                "desc='" + desc + '\'' +
                '}';
    }
}

// 使用Java的API来实现克隆
class ConcretePrototype implements Cloneable {
    String desc;

    public ConcretePrototype(String desc) {
        System.out.println("走进了构造方法");
        this.desc = desc;
    }

    @Override
    // super.clone()方法直接从堆内存中以二进制流的方式进行复制，重新分配一块新的内存
    protected ConcretePrototype clone() {
        ConcretePrototype concretePrototype;
        try {
            concretePrototype = (ConcretePrototype) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
        return concretePrototype;
    }

    @Override
    public String toString() {
        return "ConcretePrototype{" +
                "desc='" + desc + '\'' +
                '}';
    }
}

@Data
@ToString
/**
 * // 在日常开发中，使用super.clone()并不能满足所有的需求，如果类中存在引用类对象
 * // 原型对象与克隆对象的该属性会指向同一个对象引用
 * 这也是我们常说的浅拷贝
 */
class ConcretePrototypeA implements Cloneable {
    private int age;
    private String name;
    private List<String> hobbies;

    @Override
    protected ConcretePrototypeA clone() {
        ConcretePrototypeA prototypeA;
        try {
            prototypeA = (ConcretePrototypeA) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
        return prototypeA;
    }
}

/**
 * 使用序列化实现深克隆
 */
@Data
@ToString
class ConPrototypeDeep implements Cloneable, Serializable {
    private int age;
    private String name;
    private List<String> hobbies;

    @Override
    protected ConPrototypeDeep clone() {
        ConPrototypeDeep con;
        try {
            con = (ConPrototypeDeep) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
        return con;
    }

    public ConPrototypeDeep deepClone() {
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);

            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);
            return (ConPrototypeDeep) ois.readObject();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}